//-----------------------------------------------------------------------------
// File: TypeSerializer.cs
//
// Copyright (c) Dmitry Shuklin. All rights reserved.
//-----------------------------------------------------------------------------
using System;

namespace ObjectiveDraw.Platform.Serialization
{
	/// <summary>
	/// Summary description for TypeSerializer.
	/// </summary>
	public class TypeSerializer
	{
		private class PropertyMapping
		{
			public PropertyMapping(System.ComponentModel.PropertyDescriptor propertyDescriptor, string attributeName, IPropertyFormatter propertyFormatter, object [] formatterParameters)
			{
				this.PropertyDescriptor = propertyDescriptor;
				this.AttributeName = attributeName;
				this.PropertyFormatter = propertyFormatter;
				this.FormatterParameters = formatterParameters;
			}
			public System.ComponentModel.PropertyDescriptor PropertyDescriptor;
			public string AttributeName;
			public IPropertyFormatter PropertyFormatter;
			public object [] FormatterParameters;
		}

		private class FallbackCache
		{
			public FallbackCache(Cerebrum.ObjectHandle[] attributes)
			{
				this.Attributes = attributes;
				this.Precedent = null;
			}
			public FallbackCache Precedent;
			public Cerebrum.ObjectHandle[] Attributes;
		}

		private System.Type m_Type;
		private PropertyMapping[] m_PropertiesMapping;
		private System.Collections.Hashtable m_PropertiesHash;

		private TypeSerializer(System.Type type, PropertyMapping[] propertiesMapping)
		{
			m_Type = type;
			m_PropertiesMapping = propertiesMapping;
			m_PropertiesHash = null;
		}

		protected static object m_KnownHandlesKey = new object();
		protected static System.Collections.Hashtable m_KnownTypes = new System.Collections.Hashtable();
		protected static ObjectFormatter m_ObjectFormatter = new ObjectFormatter();
		protected static System.Collections.Hashtable m_Formatters = new System.Collections.Hashtable();

		private static void GetRecursiveProperties(bool isVirtual, System.Type type, System.Collections.Hashtable properties)
		{
			System.Reflection.PropertyInfo [] props = type.GetProperties();
			foreach(System.Reflection.PropertyInfo prop in props)
			{
				object [] attrs = prop.GetCustomAttributes(true);
				System.Attribute [] arr = null;
				foreach(object attrtmp in attrs)
				{
					ObjectiveDraw.Platform.Serialization.SerializablePropertyAttribute attr = attrtmp as ObjectiveDraw.Platform.Serialization.SerializablePropertyAttribute;
					if(attr!=null)
					{
						if(!properties.ContainsKey(attr.AttributeName))
						{
							if(arr==null)
							{
								arr = new System.Attribute [attrs.Length];
								attrs.CopyTo(arr, 0);
							}
							if(!isVirtual)
							{
								properties.Add(attr.AttributeName, new ReflectionPropertyDescriptor(attr.AttributeName, arr, prop));
							}
							else
							{
								properties.Add(attr.AttributeName, new VirtualPropertyDescriptor(attr.AttributeName, arr));
							}
						}
					}
				}
			}
			foreach(System.Attribute attr in type.GetCustomAttributes(typeof(ObjectiveDraw.Platform.Serialization.DependentTypeAttribute), true))
			{
				ObjectiveDraw.Platform.Serialization.DependentTypeAttribute dta = attr as ObjectiveDraw.Platform.Serialization.DependentTypeAttribute;
				if(dta!=null)
				{
					TypeSerializer.GetRecursiveProperties(true, System.Type.GetType(dta.DependentType), properties);
				}
			}
		}

		/*private static void GetRecursiveProperties(bool isVirtual, System.Type type, System.Collections.Hashtable properties)
		{
			System.ComponentModel.PropertyDescriptorCollection props = System.ComponentModel.TypeDescriptor.GetProperties(type);
			foreach(System.ComponentModel.PropertyDescriptor prop in props)
			{
				ObjectiveDraw.Platform.Serialization.SerializablePropertyAttribute attr = (ObjectiveDraw.Platform.Serialization.SerializablePropertyAttribute)prop.Attributes[typeof(ObjectiveDraw.Platform.Serialization.SerializablePropertyAttribute)];
				if(attr!=null)
				{
					if(!properties.ContainsKey(attr.AttributeName))
					{
						if(!isVirtual)
						{
							properties.Add(attr.AttributeName, prop);
						}
						else
						{
							System.Attribute [] arr = new System.Attribute [prop.Attributes.Count];
							prop.Attributes.CopyTo(arr, 0);
							properties.Add(attr.AttributeName, new VirtualPropertyDescriptor(attr.AttributeName, arr));
						}
					}
				}
			}
			foreach(System.Attribute attr in System.ComponentModel.TypeDescriptor.GetAttributes(type))
			{
				ObjectiveDraw.Platform.Serialization.DependentTypeAttribute dta = attr as ObjectiveDraw.Platform.Serialization.DependentTypeAttribute;
				if(dta!=null)
				{
					TypeSerializer.GetRecursiveProperties(true, System.Type.GetType(dta.DependentType), properties);
				}
			}
		}*/

		public static TypeSerializer GetSerializer(System.Type type)
		{
			TypeSerializer ts = m_KnownTypes[type] as TypeSerializer;
			if(ts==null)
			{
				System.Collections.Hashtable properties = new System.Collections.Hashtable();
				TypeSerializer.GetRecursiveProperties(false, type, properties);
				System.Collections.ArrayList list = new System.Collections.ArrayList();
				foreach(System.Collections.DictionaryEntry propentry in properties)
				{
					System.ComponentModel.PropertyDescriptor prop = (System.ComponentModel.PropertyDescriptor)propentry.Value;
					object frmtAttr = prop.Attributes[typeof(ObjectiveDraw.Platform.Serialization.FormatterAttribute)];
					IPropertyFormatter formatter = null;
					object [] parameters = null;
					if(frmtAttr!=null)
					{
						FormatterAttribute frmtAttr2 = (FormatterAttribute)frmtAttr;
						string formatterType = frmtAttr2.FormatterType;
						formatter = m_Formatters[formatterType] as IPropertyFormatter;
						if(formatter==null)
						{
							formatter = Activator.CreateInstance(System.Type.GetType(formatterType)) as IPropertyFormatter;
							m_Formatters[formatterType] = formatter;
						}
						parameters = frmtAttr2.Parameters;
					}
					if(formatter==null)
					{
						formatter = m_ObjectFormatter;
					}

					list.Add(new PropertyMapping(prop, (string)propentry.Key, formatter, parameters));
					
				}
				ts = new TypeSerializer(type, (PropertyMapping[])list.ToArray(typeof(PropertyMapping)));
				m_KnownTypes[type] = ts;
			}
			return ts;
		}

		public static Cerebrum.ObjectHandle CreateAttribute(Cerebrum.Integrator.DomainContext context, string attrName, Cerebrum.ObjectHandle typeId)
		{
			Cerebrum.ObjectHandle h = Cerebrum.Management.Utilites.ResolveHandle(context, "Attributes", "Name", attrName);
			if(h==Cerebrum.ObjectHandle.Null)
			{
				using(Cerebrum.Data.TableView view = Cerebrum.Management.Utilites.GetView(context, "Attributes"))
				{
					Cerebrum.Data.ComponentItemView item = view.AddNew();
					h = item.ObjectHandle;
					item["Name"] = attrName;
					item["DefaultTypeId"] = typeId;
				}
				Cerebrum.Management.Utilites.RefreshHandle(context, "Attributes", "Name", h);
			}
			return h;
		}
		private static Cerebrum.ObjectHandle[] GetAttributes(Cerebrum.Integrator.DomainContext context, TypeSerializer ts)
		{
			Cerebrum.ObjectHandle[] handles = context.Properties[ts] as Cerebrum.ObjectHandle[];
			if(handles==null)
			{
				handles = new Cerebrum.ObjectHandle[ts.m_PropertiesMapping.Length];
				for(int i=0;i<ts.m_PropertiesMapping.Length;i++)
				{
					PropertyMapping pm = ts.m_PropertiesMapping[i];

					handles[i] = CreateAttribute(context, pm.AttributeName, Cerebrum.Specialized.Concepts.ScalarNodeTypeId);
				}
				context.Properties[ts] = handles;
			}
			return handles;
		}

		public static void Serialize(FallbackChain storage, object instance)
		{
			TypeSerializer ts = TypeSerializer.GetSerializer(instance.GetType());

			FallbackCache rootCache = new FallbackCache(TypeSerializer.GetAttributes(Cerebrum.Integrator.DomainContext.FromConnector(storage.Container as Cerebrum.IConnector), ts));

			for(int i=0;i<ts.m_PropertiesMapping.Length;i++)
			{
				Cerebrum.IComposite co2;
				co2 = null;
				try
				{
					PropertyMapping pm = ts.m_PropertiesMapping[i];

					object o1 = pm.PropertyDescriptor.GetValue(instance);
					object o2 = null;
					FallbackChain precedentEntry = storage.Precedent;
					FallbackCache currentCache = rootCache;
					while(precedentEntry!=null && o2==null && precedentEntry.Container!=null)
					{
						if(co2!=null)
						{
							co2.Dispose();
							co2 = null;
						}
						if(currentCache.Precedent==null)
						{
							currentCache.Precedent = new FallbackCache(TypeSerializer.GetAttributes(Cerebrum.Integrator.DomainContext.FromConnector(precedentEntry.Container as Cerebrum.IConnector), ts));
						}
						co2 = precedentEntry.Container.AttachConnector(currentCache.Precedent.Attributes[i]);
						//o2 = Cerebrum.Integrator.Utilites.ObtainComponent(precedentEntry.Container, );
						if(co2!=null)
						{
							o2 = co2.Component;
						}
						else
						{
							o2 = null;
						}

						precedentEntry = precedentEntry.Precedent;
						currentCache = currentCache.Precedent;
					}
					if(o2!=null)
					{
						o2 = pm.PropertyFormatter.DeserializeValue(o2, pm.FormatterParameters);
					}
					if(o1!=null && (o2==null || !o2.Equals(o1)))
					{
						Cerebrum.Integrator.Utilites.UpdateComponent(storage.Container, rootCache.Attributes[i], pm.PropertyFormatter.SerializeValue(o1, pm.FormatterParameters));
					}
					else
					{
						storage.Container.RemoveConnector(rootCache.Attributes[i]);
					}
				}
				finally
				{
					if(co2!=null)
					{
						co2.Dispose();
						co2 = null;
					}
				}
			}
		}

		public static void Deserialize(FallbackChain storage, object instance)
		{
			TypeSerializer ts = TypeSerializer.GetSerializer(instance.GetType());

			FallbackCache rootCache = new FallbackCache(TypeSerializer.GetAttributes(Cerebrum.Integrator.DomainContext.FromConnector(storage.Container as Cerebrum.IConnector), ts));

			for(int i=0;i<ts.m_PropertiesMapping.Length;i++)
			{
				Cerebrum.IComposite co2;
				co2 = null;
				try
				{
					PropertyMapping pm = ts.m_PropertiesMapping[i];

					object o = null;
					FallbackChain objectEntry = storage;
					FallbackCache currCache = rootCache;
					FallbackCache prevCache = currCache.Precedent;
					do
					{
						if(co2!=null)
						{
							co2.Dispose();
							co2 = null;
						}

						if(currCache==null)
						{
							currCache = new FallbackCache(TypeSerializer.GetAttributes(Cerebrum.Integrator.DomainContext.FromConnector(objectEntry.Container as Cerebrum.IConnector), ts));
							prevCache.Precedent = currCache;
						}
						co2 = objectEntry.Container.AttachConnector(currCache.Attributes[i]);
						if(co2!=null)
						{
							o = co2.Component;
						}
						else
						{
							o = null;
						}
						//o = Cerebrum.Integrator.Utilites.ObtainComponent(objectEntry.Container, currCache.Attributes[i]);
						objectEntry = objectEntry.Precedent;
						prevCache = currCache;
						currCache = prevCache.Precedent;
					}
					while(o==null && objectEntry!=null && objectEntry.Container!=null);

					if(o!=null)
						pm.PropertyDescriptor.SetValue(instance, pm.PropertyFormatter.DeserializeValue(o, pm.FormatterParameters));
				}
				finally
				{
					if(co2!=null)
					{
						co2.Dispose();
						co2 = null;
					}
				}
			}
		}

		private System.Collections.Hashtable PropertiesHash
		{
			get
			{
				if(this.m_PropertiesHash==null)
				{
					this.m_PropertiesHash = new System.Collections.Hashtable();
					for(int i=0;i < this.m_PropertiesMapping.Length; i++)
					{
						PropertyMapping pm = this.m_PropertiesMapping[i];
						this.m_PropertiesHash.Add(pm.AttributeName, i);
					}
				}
				return this.m_PropertiesHash;
			}
		}

		public System.ComponentModel.PropertyDescriptor FindProperty(string attribute)
		{
			object index = this.PropertiesHash[attribute];
			System.ComponentModel.PropertyDescriptor pi = (index is int)?this.m_PropertiesMapping[(int)index].PropertyDescriptor:null;
			return pi;
		}

		public System.ComponentModel.PropertyDescriptorCollection GetProperties()
		{
			System.ComponentModel.PropertyDescriptor[] pds = new System.ComponentModel.PropertyDescriptor[this.m_PropertiesMapping.Length];
			for(int i=0;i<this.m_PropertiesMapping.Length;i++)
			{
				PropertyMapping pm = this.m_PropertiesMapping[i];
				pds[i] = pm.PropertyDescriptor;
			}
			return new System.ComponentModel.PropertyDescriptorCollection(pds);
		}

		public static object GetValue(ObjectiveDraw.Platform.IDrawerComponent instance, string attribute, bool fallback, object defaultValue)
		{
			/*Cerebrum.Integrator.DomainContext dc = instance.DomainContext;
			using(Cerebrum.IComposite connector = dc.AttachConnector(instance.ObjectHandle))
			{
				--o = Cerebrum.Integrator.Utilites.ObtainComponent(connector as Cerebrum.IContainer, Cerebrum.Management.Utilites.ResolveHandle(dc, "Attributes", "Name", attribute));
				if(o==null && fallback)
				{
					ObjectiveDraw.Platform.IDrawerComponent owner = instance.OwnerComponent;
					o = owner==null?null:TypeSerializer.GetValue(owner, attribute, fallback);
				}
				return o;
			}*/
			object v = null;
			if(instance!=null)
			{
				TypeSerializer ts = TypeSerializer.GetSerializer(instance.GetType());
				object index = ts.PropertiesHash[attribute];
				System.ComponentModel.PropertyDescriptor pi = (index is int)?ts.m_PropertiesMapping[(int)index].PropertyDescriptor:null;
				if(pi != null)
				{
					v = pi.GetValue(instance);
				}
				if(v==null)
				{
					if(fallback)
					{
						ObjectiveDraw.Platform.IDrawerComponent owner = instance.OwnerComponent;
						if(owner!=null)
						{
							v = TypeSerializer.GetValue(owner, attribute, fallback, defaultValue);
						}
						else
						{
							v = null;
						}
					}
					else
					{
						v = null;
					}
				}
			}
			if(v==null)
			{
				v = defaultValue;
			}
			return v;
		}
		public static void SetValue(ObjectiveDraw.Platform.IDrawerComponent instance, string attribute, bool recursive, object value)
		{
			/*Cerebrum.Integrator.DomainContext dc = instance.DomainContext;
			using(Cerebrum.IComposite connector = dc.AttachConnector(instance.ObjectHandle))
			{
				object o2 = null;
				ObjectiveDraw.Platform.IDrawerComponent owner = instance.OwnerComponent;
				if(fallback && owner!=null)
				{
				}
				if(o2==null || !o2.Equals(o1))
				{
					Cerebrum.Integrator.Utilites.UpdateComponent(connector as Cerebrum.IContainer, Cerebrum.Management.Utilites.ResolveHandle(dc, "Attributes", "Name", attribute), value);
				}
				else
				{
					(connector as Cerebrum.IContainer).RemoveConnector(h);
				}
			}*/
			TypeSerializer ts = TypeSerializer.GetSerializer(instance.GetType());
			object index = ts.PropertiesHash[attribute];
			System.ComponentModel.PropertyDescriptor pi = (index is int)?ts.m_PropertiesMapping[(int)index].PropertyDescriptor:null;
			if(pi!=null)
				pi.SetValue(instance, value);
		}
		public static bool IsDefault(ObjectiveDraw.Platform.IDrawerComponent instance, string attribute)
		{
			object v1 = null;
			object v2 = null;
			if(instance!=null)
			{
				TypeSerializer ts = TypeSerializer.GetSerializer(instance.GetType());
				object index = ts.PropertiesHash[attribute];
				System.ComponentModel.PropertyDescriptor pi = (index is int)?ts.m_PropertiesMapping[(int)index].PropertyDescriptor:null;
				if(pi != null)
				{
					v1 = pi.GetValue(instance);
				}

				ObjectiveDraw.Platform.IDrawerComponent owner = instance.OwnerComponent;
				if(owner!=null)
				{
					v2 = TypeSerializer.GetValue(owner, attribute, true, null);
				}
			}

			if(v1!=null && (v2==null || !v2.Equals(v1)))
			{
				return false;
			}
			else
			{
				return true;
			}
		}
		public static void InitValues(ObjectiveDraw.Platform.IDrawerComponent instance)
		{
			ObjectiveDraw.Platform.IDrawerComponent owner = instance.OwnerComponent;
			if(owner!=null)
			{
				TypeSerializer ts = TypeSerializer.GetSerializer(instance.GetType());

				for(int i=0;i<ts.m_PropertiesMapping.Length;i++)
				{
					PropertyMapping pm = ts.m_PropertiesMapping[i];

					pm.PropertyDescriptor.SetValue(instance, TypeSerializer.GetValue(owner, pm.AttributeName, true, pm.PropertyDescriptor.GetValue(instance)));
				}
			}
		}
	}
}
